/**************************************************************************//**
 * @file     drv_uartX.c
 * @version  V1.00
 * $Revision: 1 $
 * $Date: 21/12/28 14:04p $
 * @brief    NUC970 UART driver source file
 *
 * @note
 * SPDX-License-Identifier: Apache-2.0
 * @Copyright (C) 2021 THEWON. All rights reserved.
*****************************************************************************/
#include <rtconfig.h>

#ifdef RT_USING_SERIAL

#include <rtdevice.h>
#include "drv_uartX.h"
#include "drv_sys.h"
#include "nuc970_mfp.h"

//#define DRV_DEBUG
#define LOG_TAG             "drv.uart"
#include <drv_log.h>

#if !defined(BSP_USING_UART0) && !defined(BSP_USING_UART1) && !defined(BSP_USING_UART2) && !defined(BSP_USING_UART3) && \
    !defined(BSP_USING_UART4) && !defined(BSP_USING_UART5) && !defined(BSP_USING_UART6) && \
    !defined(BSP_USING_UART7) && !defined(BSP_USING_UART8) && !defined(BSP_USING_UART9) && \
    !defined(BSP_USING_UARTA)
#error "Please define at least one BSP_USING_UARTx"
/* this driver can be disabled at menuconfig -> RT-Thread Components -> Device Drivers */
#endif

static int nu_uart_flush(struct rt_serial_device *serial);

static void nu_uart_isr0(int vector, void *param);
static void nu_uart_isr1(int vector, void *param);

static struct nu_uart_config uart_config[] =
{
#ifdef BSP_USING_UART0
    UART0_CONFIG,
#endif
#ifdef BSP_USING_UART1
    UART1_CONFIG,
#endif
#ifdef BSP_USING_UART2
    UART2_CONFIG,
#endif
#ifdef BSP_USING_UART3
    UART3_CONFIG,
#endif
#ifdef BSP_USING_UART4
    UART4_CONFIG,
#endif
#ifdef BSP_USING_UART5
    UART5_CONFIG,
#endif
#ifdef BSP_USING_UART6
    UART6_CONFIG,
#endif
#ifdef BSP_USING_UART7
    UART7_CONFIG,
#endif
#ifdef BSP_USING_UART8
    UART8_CONFIG,
#endif
#ifdef BSP_USING_UART9
    UART9_CONFIG,
#endif
#ifdef BSP_USING_UARTA
    UARTA_CONFIG,
#endif
};

static struct nu_uart_device uart_obj[sizeof(uart_config) / sizeof(uart_config[0])] = {0};

// for uart 0 3 5 7 9
static void nu_uart_isr0(int vector, void *param)
{
    struct rt_serial_device *serial;
    UART_HandleTypeDef *huart;
    UINT32 volatile uRegISR, uRegFSR, uRegMSR;

    serial = (struct rt_serial_device *)param;
    huart = &((struct nu_uart_device*)serial)->handle;

    uRegISR = huart->Instance->ISR & 0xFFFF;
    uRegFSR = huart->Instance->FSR & 0xFFFF;

    if (uRegISR & (UART_ISR_RDA_INT_Msk | UART_ISR_TOUT_INT_Msk)) {   // Received Data Available interrupt
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
    }

    if (uRegISR & UART_ISR_THRE_INT_Msk) {                            // Transmit Holding Register Empty interrupt
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE | (16<<8));
    }

    if (uRegISR & UART_ISR_MODEM_INT_Msk) {
        uRegMSR = huart->Instance->MSR;
        uRegMSR |= UART_MSR_DCTSF_Msk;
        huart->Instance->MSR = uRegMSR;
    }

    if (uRegISR & UART_ISR_BUF_ERR_INT_Msk) {
        if (uRegFSR & (UART_FSR_TX_OVER_IF_Msk)) {
            huart->Instance->FSR = UART_FSR_TX_OVER_IF_Msk;
        }
        if (uRegFSR & (UART_FSR_RX_OVER_IF_Msk)) {
            huart->Instance->FSR = UART_FSR_RX_OVER_IF_Msk;
        }
    }

    if (uRegFSR & (UART_FSR_BIF_Msk | UART_FSR_FEF_Msk | UART_FSR_PEF_Msk | UART_FSR_RX_OVER_IF_Msk | UART_FSR_TX_OVER_IF_Msk)) {
        huart->Instance->FSR = (UART_FSR_BIF_Msk | UART_FSR_FEF_Msk | UART_FSR_PEF_Msk | UART_FSR_RX_OVER_IF_Msk | UART_FSR_TX_OVER_IF_Msk);
    }
}

// for uart 1 2 4 6 8 10
static void nu_uart_isr1(int vector, void *param)
{
    struct rt_serial_device *serial;
    UART_HandleTypeDef *huart;
    UINT32 volatile uRegISR, uRegFSR, uRegMSR;

    serial = (struct rt_serial_device *)param;
    huart = &((struct nu_uart_device*)serial)->handle;

    uRegISR = huart->Instance->ISR & 0xFFFF;
    uRegFSR = huart->Instance->FSR & 0xFFFF;

    if (uRegISR & (UART_ISR_RDA_INT_Msk | UART_ISR_TOUT_INT_Msk)) {   // Received Data Available interrupt
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
    }

    if (uRegISR & UART_ISR_THRE_INT_Msk) {                            // Transmit Holding Register Empty interrupt
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE | (64<<8));
    }

    if (uRegISR & UART_ISR_MODEM_INT_Msk) {
        uRegMSR = huart->Instance->MSR;
        uRegMSR |= UART_MSR_DCTSF_Msk;
        huart->Instance->MSR = uRegMSR;
    }

    if (uRegISR & UART_ISR_BUF_ERR_INT_Msk) {
        if (uRegFSR & (UART_FSR_TX_OVER_IF_Msk)) {
            huart->Instance->FSR = UART_FSR_TX_OVER_IF_Msk;
        }
        if (uRegFSR & (UART_FSR_RX_OVER_IF_Msk)) {
            huart->Instance->FSR = UART_FSR_RX_OVER_IF_Msk;
        }
    }

    if (uRegFSR & (UART_FSR_BIF_Msk | UART_FSR_FEF_Msk | UART_FSR_PEF_Msk | UART_FSR_RX_OVER_IF_Msk | UART_FSR_TX_OVER_IF_Msk)) {
        huart->Instance->FSR = (UART_FSR_BIF_Msk | UART_FSR_FEF_Msk | UART_FSR_PEF_Msk | UART_FSR_RX_OVER_IF_Msk | UART_FSR_TX_OVER_IF_Msk);
    }
}

/**
 * Configure uart port
 */
static rt_err_t nu_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct nu_uart_config *uart_config;
    UART_HandleTypeDef *huart;

    RT_ASSERT(serial != RT_NULL);
    /* Check baudrate */
    RT_ASSERT(cfg->baud_rate != 0);

    uart_config = ((struct nu_uart_device*)serial)->uart_config;
    huart = &((struct nu_uart_device*)serial)->handle;
    huart->Instance = uart_config->Instance;
    huart->BaudRate = cfg->baud_rate;

    /* Check word len */
    switch (cfg->data_bits) {
    case DATA_BITS_5:
        huart->DataBits = NUC_DATA_BITS_5;
    break;
    case DATA_BITS_6:
        huart->DataBits = NUC_DATA_BITS_6;
    break;
    case DATA_BITS_7:
        huart->DataBits = NUC_DATA_BITS_7;
    break;
    case DATA_BITS_8:
        huart->DataBits = NUC_DATA_BITS_8;
    break;
    default:
        huart->DataBits = NUC_DATA_BITS_8;
    break;
    }

    /* Check stop bit */
    switch (cfg->stop_bits) {
    case STOP_BITS_1:
        huart->StopBits = NUC_STOP_BITS_1;
    break;
    case STOP_BITS_2:
        huart->StopBits = NUC_STOP_BITS_2;
    break;
    default:
        huart->StopBits = NUC_STOP_BITS_1;
    break;
    }

    /* Check parity */
    switch (cfg->parity)
    {
    case PARITY_NONE:
        huart->Parity = NUC_PARITY_NONE;
    break;
    case PARITY_ODD:
        huart->Parity = NUC_PARITY_ODD;
    break;
    case PARITY_EVEN:
        huart->Parity = NUC_PARITY_EVEN;
    break;
    default:
        huart->Parity = NUC_PARITY_NONE;
    break;
    }

    /* Configure UART Baudrate wordLength... */
    Uart_Config(huart);

    return RT_EOK;
}

/**
 * Initialize uart port
 */
static rt_err_t nu_uart_init(struct rt_serial_device *serial)
{
    struct nu_uart_config *uart_config;
    UART_HandleTypeDef *huart;

    RT_ASSERT(serial != RT_NULL);

    uart_config = ((struct nu_uart_device*)serial)->uart_config;
    huart = &((struct nu_uart_device*)serial)->handle;
    huart->Instance = uart_config->Instance;
    huart->BaudRate = serial->config.baud_rate;

    /* Check word len */
    switch (serial->config.data_bits) {
    case DATA_BITS_5:
        huart->DataBits = NUC_DATA_BITS_5;
    break;
    case DATA_BITS_6:
        huart->DataBits = NUC_DATA_BITS_6;
    break;
    case DATA_BITS_7:
        huart->DataBits = NUC_DATA_BITS_7;
    break;
    case DATA_BITS_8:
        huart->DataBits = NUC_DATA_BITS_8;
    break;
    default:
        huart->DataBits = NUC_DATA_BITS_8;
    break;
    }

    /* Check stop bit */
    switch (serial->config.stop_bits) {
    case STOP_BITS_1:
        huart->StopBits = NUC_STOP_BITS_1;
    break;
    case STOP_BITS_2:
        huart->StopBits = NUC_STOP_BITS_2;
    break;
    default:
        huart->StopBits = NUC_STOP_BITS_1;
    break;
    }

    /* Check parity */
    switch (serial->config.parity)
    {
    case PARITY_NONE:
        huart->Parity = NUC_PARITY_NONE;
    break;
    case PARITY_ODD:
        huart->Parity = NUC_PARITY_ODD;
    break;
    case PARITY_EVEN:
        huart->Parity = NUC_PARITY_EVEN;
    break;
    default:
        huart->Parity = NUC_PARITY_NONE;
    break;
    }

    MFP_UARTInit(huart->Instance);

    nu_uart_flush(serial);

    /* Open Uart and set UART default Baudrate */
    Uart_Init(huart);

    return RT_EOK;
}

static rt_err_t nu_uart_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct nu_uart_config *uart_config;
    rt_ubase_t ctrl_arg = (rt_ubase_t)arg;
    UART_HandleTypeDef *huart;
    rt_uint32_t reg;

    RT_ASSERT(serial != RT_NULL);

    uart_config = ((struct nu_uart_device*)serial)->uart_config;
    huart = &((struct nu_uart_device*)serial)->handle;

    switch (cmd) {
    case RT_DEVICE_CTRL_OPEN:
        /* Enable interrupt. */
        rt_hw_interrupt_install(uart_config->irq_type, uart_config->isr_cb, (void *)(serial), uart_config->name);
        rt_hw_interrupt_set_priority(uart_config->irq_type, IRQ_LEVEL_2);
        rt_hw_interrupt_set_type(uart_config->irq_type, HIGH_LEVEL_SENSITIVE);
        rt_hw_interrupt_umask(uart_config->irq_type);
    break;
    case RT_DEVICE_CTRL_CLR_INT:
        /* disable interrupt */
        reg = huart->Instance->IER;
        reg &= ~UART_IER_BUF_ERR_IEN_Msk;
        if (ctrl_arg & RT_DEVICE_FLAG_INT_TX) {
            reg &= ~UART_IER_THRE_IEN_Msk;
        }
        if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) {
            reg &= ~UART_IER_RDA_IEN_Msk;
            reg &= ~UART_IER_RTO_IEN_Msk;
            reg &= ~UART_IER_TIME_OUT_EN_Msk;
        }
        huart->Instance->IER = reg;

#ifdef RT_SERIAL_USING_DMA
        /* disable DMA */
#endif
    break;
    case RT_DEVICE_CTRL_SET_INT:
        /* enable interrupt */
        reg = huart->Instance->IER;
        reg |= UART_IER_BUF_ERR_IEN_Msk;
        if (ctrl_arg & RT_DEVICE_FLAG_INT_TX) {
            reg |= UART_IER_THRE_IEN_Msk;
        }
        if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) {
            reg |= UART_IER_RDA_IEN_Msk;
            reg |= UART_IER_RTO_IEN_Msk;
            reg |= UART_IER_TIME_OUT_EN_Msk;
        }
        huart->Instance->IER = reg;
    break;
    case RT_DEVICE_CTRL_CONFIG:
#ifdef RT_SERIAL_USING_DMA
#endif
    break;
    case RT_DEVICE_CTRL_CLOSE:
        /* disable interrupt */
        rt_hw_interrupt_mask(uart_config->irq_type);
        huart->Instance->IER = 0;
        MFP_UARTDeInit(huart->Instance);
    break;
    default :
    break;
    }
    return RT_EOK;
}

static int nu_uart_putc(struct rt_serial_device *serial, char c)
{
    UART_HandleTypeDef *huart;

    RT_ASSERT(serial != RT_NULL);

    huart = &((struct nu_uart_device*)serial)->handle;
    while(Uart_Tx_isFull(huart));
    Uart_Write(huart, c);
    return 1;
}

static int nu_uart_getc(struct rt_serial_device *serial)
{
    int ch;
    UART_HandleTypeDef *huart;

    RT_ASSERT(serial != RT_NULL);

    huart = &((struct nu_uart_device*)serial)->handle;

    if (Uart_Rx_isEmpty(huart)) {
        return -1;
    }
    ch = Uart_Read_Byte(huart);

    return ch;
}

static int nu_uart_flush(struct rt_serial_device *serial)
{
    UART_HandleTypeDef *huart;

    RT_ASSERT(serial != RT_NULL);

    huart = &((struct nu_uart_device*)serial)->handle;
    while(!Uart_Tx_isEmpty(huart)){;}

    return 1;
}

static void nu_start_tx(struct rt_serial_device *serial)
{
    UART_HandleTypeDef *huart;
    rt_uint32_t reg;

    RT_ASSERT(serial != RT_NULL);

    huart = &((struct nu_uart_device*)serial)->handle;

    /* enable interrupt */
    reg = huart->Instance->IER;
    reg |= UART_IER_THRE_IEN_Msk;
    huart->Instance->IER = reg;
}

static void nu_stop_tx(struct rt_serial_device *serial)
{
    UART_HandleTypeDef *huart;
    rt_uint32_t reg;

    RT_ASSERT(serial != RT_NULL);

    huart = &((struct nu_uart_device*)serial)->handle;

    /* disable interrupt */
    reg = huart->Instance->IER;
    reg &= ~UART_IER_THRE_IEN_Msk;
    huart->Instance->IER = reg;
}

static void nu_enable_interrupt(struct rt_serial_device *serial)
{
    struct nu_uart_config *uart_config;

    RT_ASSERT(serial != RT_NULL);

    uart_config = ((struct nu_uart_device*)serial)->uart_config;

    rt_hw_interrupt_umask(uart_config->irq_type);
}

static void nu_disable_interrupt(struct rt_serial_device *serial)
{
    struct nu_uart_config *uart_config;

    RT_ASSERT(serial != RT_NULL);

    uart_config = ((struct nu_uart_device*)serial)->uart_config;

    rt_hw_interrupt_mask(uart_config->irq_type);
}

static const struct rt_uart_ops nu_uart_ops =
{
    .init = nu_uart_init,
    .configure = nu_uart_configure,
    .control = nu_uart_control,
    .putc = nu_uart_putc,
    .getc = nu_uart_getc,
    .flush = nu_uart_flush,
    .start_tx = nu_start_tx,
    .stop_tx = nu_stop_tx,
    .enable_interrupt = nu_enable_interrupt,
    .disable_interrupt = nu_disable_interrupt,
};

int rt_hw_usart_init(void)
{
    rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct nu_uart_device);
    rt_err_t result = 0;
    int i;

    for (i = 0; i < obj_num; i++)
    {
        /* init UART object */
        uart_obj[i].uart_config = &uart_config[i];
        uart_obj[i].serial.ops = &nu_uart_ops;

        /* register UART device */
        result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].uart_config->name,
                                       RT_DEVICE_FLAG_RDWR
                                       | RT_DEVICE_FLAG_INT_RX
                                       | RT_DEVICE_FLAG_INT_TX
                                       , NULL);
        RT_ASSERT(result == RT_EOK);
    }

    return result;
}

#endif /* RT_USING_SERIAL */
